﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.CodeDom.Compiler;
using PDOGenerator.MapTypes;

namespace PDOGenerator.Generators
{
    /// <summary>
    /// NHibernate XML generator.
    /// </summary>
    class ToXML : ClassGenerator
    {
        private Project Proj;
        internal ToXML(Project p)
        {
            Proj = p;
        }

        public override string Generate(PDOGenerator.MapTypes.TableMap Table)
        {
            StringWriter sw = new StringWriter();
            IndentedTextWriter tw = new IndentedTextWriter(sw, "\t");
            tw.Indent = 0;

            GenerateHeaderArea(tw, Table);

            GenerateKey(tw, Table);

            foreach (ColumnMap col in Table.Columns.Values)
            {
                if (!col.IsPK)
                    GenerateColumn(tw, col);
            }

            GenerateFooterArea(tw);

            return sw.ToString();
        }

        private void GenerateColumn(IndentedTextWriter tw, ColumnMap col)
        {
            string access = "access=\"field.pascalcase-underscore\" ";
            string NotNull = "";

            if (!col.Nullable)
                NotNull = "not-null=\"true\" ";

            if (!col.IsCollection)
            {
                string RO;
                if (col.ReadOnly)
                    RO = "update=\"false\" insert=\"false\" ";
                else
                {
                    if (col.IsFK && (col.ForeignKeyType == ForeignKeyTypes.ObjectAndReadonlyKey || col.ForeignKeyType == ForeignKeyTypes.ObjectOnly))
                        RO = "cascade=\"save-update\" ";
                    else
                        RO = "";
                }

                if (!col.IsFK || col.ForeignKeyType == ForeignKeyTypes.KeyOnly || col.ForeignKeyType == ForeignKeyTypes.KeyAndReadonlyObject)
                    tw.WriteLine("<property name=\"{0}\" column=\"{2}\" {3}{4}{5}/>", col.PropertyName, col.DataType, col.ColumnName, access, NotNull, RO);
                else
                    tw.WriteLine("<many-to-one name=\"{0}\" column=\"{2}\" {5}{3}{4}/>", col.ObjectPropertyName, 2, col.ColumnName, access, NotNull, RO);

                if (col.IsFK && col.ForeignKeyType == ForeignKeyTypes.KeyAndReadonlyObject)
                    tw.WriteLine("<many-to-one name=\"{0}\" column=\"{2}\" update=\"false\" insert=\"false\" {3}{4}/>", col.ObjectPropertyName, 2, col.ColumnName, access, NotNull);
            }
            else
            {
                string cType = Enum.GetName(typeof(CollectionTypes), col.CollectionType).ToLower();
                string s = "<" + cType;
                s += " name=\"" + col.PropertyName + "\"";
                s += " lazy=\"true\"";
                s += " cascade=\"all-delete-orphan\"";
                s += " " + access;
                s += ">";
                tw.WriteLine(s);
                tw.Indent++;

                tw.WriteLine("<key column=\"" + col.CollectionColumn + "\" />");
                if (col.CollectionType != CollectionTypes.Bag)
                    tw.WriteLine("<index column=\"" + col.IndexColumn + "\" type=\"" + Proj.AllTables[col.CollectionTableMoniker].Columns[col.CollectionColumn].DataType + "\" />");

                tw.WriteLine("<one-to-many class=\"" + Proj.NameSpace + "." + Proj.AllTables[col.CollectionTableMoniker].ClassName + "," + Proj.Assembly + "\" />");

                tw.Indent--;
                tw.WriteLine("</" + cType + ">");
            }
        }

        private void GenerateKey(IndentedTextWriter tw, TableMap Table)
        {
            if (Table.HasCompositeKey())
            {
                tw.WriteLine("<composite-id>");
                tw.Indent++;

                foreach (ColumnMap col in Table.Columns.Values)
                {
                    if (col.IsPK)
                    {
                        string access = "access=\"field.pascalcase-underscore\"";
                        tw.WriteLine("<key-property name=\"{0}\" type=\"{1}\" {3} column=\"{2}\" />", col.PropertyName, col.DataType, col.ColumnName, access);
                    }
                }

                tw.Indent--;
                tw.WriteLine("</composite-id>");
            }
            else
            {
                foreach (ColumnMap col in Table.Columns.Values)
                {
                    if (col.IsPK)
                    {
                        string access = "access=\"field.pascalcase-underscore\"";

                        tw.WriteLine("<id name=\"{0}\" type=\"{1}\" column=\"{2}\" {3}>", col.PropertyName, col.DataType, col.ColumnName, access);
                        tw.Indent++;
                        if (col.Identity)
                            tw.WriteLine("<generator class=\"native\" />");
                        else if (col.Sequence == null)
                            tw.WriteLine("<generator class=\"assigned\" />");
                        else
                        {
                            tw.WriteLine("<generator class=\"sequence\">");
                            tw.Indent++;
                            tw.WriteLine("<param name=\"sequence\">" + col.Sequence + "</param>");
                            tw.Indent--;
                            tw.WriteLine("</generator>");
                        }

                        tw.Indent--;
                        tw.WriteLine("</id>");
                    }
                }

            }
        }

        private void GenerateFooterArea(IndentedTextWriter tw)
        {
            tw.Indent--;
            tw.WriteLine("</class>");
            tw.Indent--;
            tw.WriteLine("</hibernate-mapping>");
        }

        private void GenerateHeaderArea(IndentedTextWriter tw, TableMap Table)
        {
            tw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
            tw.WriteLine("<!-- Generated by MoreGen " + DateTime.Now.ToString("d-MMM-yyyy hh:mm:ss") + " -->");
            tw.WriteLine("<hibernate-mapping xmlns=\"urn:nhibernate-mapping-2.2\" schema=\"dbo\" default-cascade=\"save-update\" auto-import=\"true\">");
            tw.Indent++;
            tw.WriteLine("<class name=\"" + Proj.NameSpace + "." + Table.ClassName + ", " + Proj.Assembly + "\" lazy=\"true\" table=\"" + Table.TableName + "\">");
            tw.Indent++;
        }
    }
}
